﻿#define  _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<vector>
#include<list>
using namespace std;

////写一个继承
//class Person
//{
//public:
//	void identity()
//	{
//		//...
//	}
//
//protected:
//	int age=18;
//	string name="张三";
//	string address="安徽";
//	string tel="1110";
//};
//
//class Teacher :public Person
//{
//public:
//	void teaching()
//	{
//		//...
//	}
//
//	void print()
//	{
//		cout << age << endl;
//		cout << tel << endl;
//		cout << name << endl;
//		cout << workid<< endl;
//	}
//
//protected:
//	string workid="123456";//工号
//};
//
//class Student :public Person
//{
//public:
//	void study()
//	{
//		//...
//	}
//
//	void print()
//	{
//		cout << age << endl;
//		cout << tel << endl;
//		cout << name << endl;
//		cout << studyid << endl;
//	}
//
//protected:
//	string studyid="00000"; //学号
//	int age = 28;
//	string name = "王五";
//	string address = "上海";
//	string tel = "999";
//};
//
//int main()
//{
//	Student s;
//	Teacher t;
//	t.print();
//	cout << endl;
//	s.print();
//	return 0;
//}

//继承方式：public,protected,private
//访问方式：public,protected,private
//对于派生类继承基类之后，成员变量的访问优先级：
/*
* 对于继承方式是private的话，无论访问方式是什么，在派生类中都是不可以访问到其成员变量/方法
* protected，在类外是不可以被访问的，但是在我们的派生类中是可以进行访问的
* 至于继承方式是public/protected。我们根据这个规则：访问的权限=Min(基类继承方式，派生类的访问方式）
* 我们一般最常用的是使用public来作为基类的继承方式，public,protected作为派生类的访问方式
*/

////使用继承来模拟实现一个stack(我们之前是使用容器适配器模拟实现出来的）
//namespace hjc
//{
//	template<class T>
//	class stack :public vector<T>
//	{
//	public:
//		void push(const T& x)
//		{
//	//我们模拟实现stack，是通过继承vector的成员方法来进行实现的
//	//我们在调用它的成员方法时，我们要使用::(作用域运算符）来进行指明是那个类中的
//	//虽然我们在实例化stack时，其基类vector也应该被实例化了，但是它是按需进行实例化的
//	//因此我们每次调用基类的成员方法时一定要使用作用域运算符来进行指明，否则那个成员方法编译器就认为未声明
//			vector<T>::push_back(x);
//		}
//
//		void pop()
//		{
//			vector<T>::pop_back();
//		}
//
//		const T& top()
//		{
//			return vector<T>::back();
//		}
//
//		bool empty()
//		{
//			return vector<T>::empty();
//		}
//	};
//}
//
//int main()
//{
//	hjc::stack<int> st; //实例化一个stack对象
//	st.push(1);
//	st.push(2);
//	st.push(3);
//
//	while (!st.empty())
//	{
//		cout << st.top() << endl;
//		st.pop();
//	}
//	return 0;
//
//}

//class Person
//{
//public:
////protected:
//	int age=18;
//	string address="安徽";
//	string name="小明";
//};
//
//class Student :public Person
//{
//public:
//protected:
//	int _NO=9; //学号
//};
//
//
//int main()
//{
//	Person pobj;
//	Student sobj;
//
//	//我们可以将派生类的对象赋值给基类（指针和引用）
//	Person* pb = &sobj;
//	Person& rp = sobj;
//	rp.age++;
//	return 0;
//}
//
///*
//* 我们只能够将派生类的对象通过引用和指针的方式赋给基类，这是一个特殊操作
//* 因为，派生类是通过基类继承过来产生的，它有基类的成员变量以及基类没有的成员变量，因此
//* 我们可以“切割派生类中那一块基类所有的那一部分”,我们把这个操作称为 切割/切片（切割兼容转换）
//* 这是基类与派生类之间所独有的一种特殊的操作方式，其他类型的对象使用引用赋值时，可能会形成临时对象
//* 那样就会使那个变量具有常性，如果我们不加const的话，就会发生报错
//*/
//
//class Person
//{
//public:
//protected:
//	int age = 18;
//	int num = 110;  //身份证号码
//};
//
//class Student :public Person
//{
//public:
//	void print()
//	{
//		cout << Person::num << endl; //身份证号码
//		cout << num << endl; //学号
//	}
//protected:
//	int num = 999;//学号
//};
//
//int main()
//{
//	Student s;
//	s.print();
//	return 0;
//}

/*
* 基类与派生类是两个不同的作用域
* 在派生类和基类中如果出现了，同名的成员变量的话，就会出现隐藏，在派生类中出现的同名成员变量
* 会对基类中的那个成员变量进行屏蔽，使用派生类中的那个同名成员变量，如果我们想要使用基类中的那个同名成员变量
* 我们可以使用  基类::成员变量 来进行显示调用
* 对于成员方法：只要成员方法的方法名相同就会进行隐藏，不会去看参数是否相同
*/

//如下例子中的fun函数就构成了隐藏关系。因为这两个函数分别是在基类和派生类中的，是两个不同的作用域
//对于函数重载是要求在同一作用域中，相同的函数名，不同的函数参数
//class A
//{
//public:
//	void fun()
//	{
//		cout << "func()" << endl;
//	}
//};
//class B : public A
//{
//public:
//	void fun(int i)
//	{
//		cout << "func(int i)" << i << endl;
//	}
//};
//int main()
//{
//	B b;
//	b.fun(10);
//	//b.fun(); 我们会发现这行代码会发生编译错误，因为，我们没有显示调用基类中的那个同名成员方法
//	//于是它调用的还是派生类中的那个同名成员方法，而派生类中的那个函数是需要参数的，因此在编译时就会发生报错了
//
//	return 0;
//};


////一个完整的基类Person（显示实现了四个默认成员函数）
//class Person
//{
//public:
//	//构造函数
//	Person(const char* name = "peter")
//		: _name(name)
//	{
//		cout << "Person()" << endl;
//	}
//	//拷贝构造函数
//	Person(const Person& p)
//		: _name(p._name)
//	{
//		cout << "Person(const Person& p)" << endl;
//	}
//	//赋值运算符重载
//	Person& operator=(const Person& p)
//	{
//		cout << "Person operator=(const Person& p)" << endl;
//		if (this != &p)
//			_name = p._name;
//
//		return *this;
//	}
//	//析构函数
//	~Person()
//	{
//		cout << "~Person()" << endl;
//	}
//protected:
//	string _name; // 姓名 
//};
//
//
//class Student : public Person
//{
//public:
//	//派生类的构造函数显示实现：我们使用初始化列表进行初始化，我们将基类中没有进行初始化的
//	//变量在初始化列表中进行初始化，然后再调用基类的构造函数
//	Student(const char* name, int num)
//		: Person(name) //类似于我们之前的匿名对象，我们之间将参数传递过去，然后它就会调用基类的构造函数
//		, _num(num)
//	{
//		cout << "Student()" << endl;
//	}
//
//	Student(const Student& s)
//		: Person(s)
//		, _num(s._num)
//	{
//		cout << "Student(const Student& s)" << endl;
//	}
//
//	Student& operator = (const Student& s)
//	{
//		cout << "Student& operator= (const Student& s)" << endl;
//		if (this != &s)
//		{
//			// 构成隐藏，所以需要显⽰调⽤ 
//			Person::operator =(s);
//			_num = s._num;
//		}
//		return *this;
//	}
//
//	//这个析构函数，在后面的多态会介绍到，这个在编译器中最后都会变为destructor()
//	//于是基类与派生类就构成了隐藏关系了，而且C++中已经规定过了，派生类的析构函数会自动调用基类的析构函数
//	//于是，我们就不要显示再去写基类的构造函数了
//	~Student()
//	{
//		cout << "~Student()" << endl;
//	}
//protected:
//	int _num; //学号 
//};
//int main()
//{
//	Student s1("jack", 18);
//	Student s2(s1);
//	Student s3("rose", 17);
//	s1 = s3;
//
//	return 0;
//}
//
///*
//* 总结上面的例子：我们可以发现，只有构造函数有时候需要我们自己来显示实现（因为派生类中的有些
//* 成员变量是基类中所没有的，因此需要我们自己去进行初始化。至于另外三种默认成员函数，我们可以
//* 直接使用默认生成的（基类的）
//* 构造函数调用顺序：先基类后派生类
//* 析构函数调用顺序：先派生类后基类
//*/

////下面是一个无法实现继承的基类
//class Base final  //我们使用了一个关键字final，那样的话，我们就能强制这个基类不能够被继承
//	//这是在C++11中新加的
//{
//public:
//	void func5() { cout << "Base::func5" << endl; }
//protected:
//	int a = 1;
//private:
//	// C++98的⽅法 
//	/*Base()   我们也可以将基类进行私有化
//	{}*/
//};
//
//class Derive :public Base
//{
//	void func4() { cout << "Derive::func4" << endl; }
//protected:
//	int b = 2;
//};
//int main()
//{
//	Base b;
//	Derive d;
//	return 0;
//}

////友元函数是不可以被继承的，也就是说，基类的友元是不可以访问派生类的保护和私有成员变量的
//class Person
//{
//public:
//	friend void Display(const Person& p, const Student& s);
//protected:
//	string _name; // 姓名 
//};
//
//class Student : public Person
//{
//public:
//	friend void Display(const Person& p, const Student& s);
//protected:
//	int _stuNum; // 学号 
//};
//void Display(const Person& p, const Student& s)
//{
//	cout << p._name << endl;
//	cout << s._stuNum << endl;
//}
//int main()
//{
//	Person p;
//	Student s;  //我们在Display函数中，调用了student类中的一个成员变量
//	//对于一个在类外定义的函数，我们是无法访问类中的成员变量的，如果想要访问我们需要将其定为友元函数
//	// 虽然我们在基类中已经将其定为了友元函数，但是友元函数是不能够继承给派生类的，因此需要我们自己
//	// 手动在派生类中再定义一个友元函数
//	// 编译报错：error C2248: “Student::_stuNum”: ⽆法访问 protected 成员 
//	// 解决⽅案：Display也变成Student 的友元即可 
//	Display(p, s);
//
//	return 0;
//}


